package hjt212

import (
	"airserver/airmodel"
	"fmt"
	"net"
	"strconv"
	"strings"
	"time"

	"github.com/astaxie/beego/config"
	"github.com/astaxie/beego/logs"
	"github.com/astaxie/beego/orm"
)

const (
	MAX_REAL_DATANUM = 5000000
	MAX_MIN_DATANUM  = 5000000
	MAX_HOUR_DATANUM = 1000000
	MAX_DAY_DATANUM  = 100000
)

//global logger
var Beelog *logs.BeeLogger

func init() {
	var logtype string
	var loglevel int
	beeconfig, err := config.NewConfig("ini", "../airconf/server.conf")
	if nil != err {
		loglevel = 7
		logtype = "console"
	} else {
		loglevel, err = beeconfig.Int("dataloglevel")
		if nil != err {
			loglevel = 7
		}
		logtype = beeconfig.String("datalogtype")
	}

	Beelog = logs.NewLogger(10000)
	Beelog.SetLevel(loglevel)
	Beelog.SetLogger(logtype)
	if logtype == "file" {
		Beelog.SetLogger(logs.AdapterFile, `{"filename":"../log/dataserver.log"}`)
	}
}

// start hjt212 server
func ServerStart() {

	go deleteDataFunc()

	beeconfig, err := config.NewConfig("ini", "../airconf/server.conf")
	if nil != err {
		panic(err)
	}

	serverstr := ":" + beeconfig.String("serverport")
	cn, err := net.Listen("tcp", serverstr)
	if err != nil {
		Beelog.Emergency("%v", err)
		return
	}

	defer cn.Close()

	for {
		cna, erra := cn.Accept()
		if erra != nil {
			Beelog.Error("%v", err)
			continue
		}

		go serverthread(cna)
	}
}

func serverthread(con net.Conn) {
	Beelog.Notice("Connectd:", con.RemoteAddr())

	defer con.Close()
	defer Beelog.Notice("Disconnectd:", con.RemoteAddr())

	var response = 1
	var qn string
	for {
		data := make([]byte, 2048)
		num, err := con.Read(data)
		if err != nil {
			Beelog.Notice("%v", err)
			return
		}

		if num <= 0 {
			continue
		}

		str := string([]byte(data[:num]))
		Beelog.Debug(str)

		if !strings.HasPrefix(str, "##") {
			continue
		}

		if !strings.HasSuffix(str, "\r\n") {
			continue
		}

		pos := strings.Index(str, "MN=")
		if -1 == pos {
			continue
		}
		substr := str[pos+len("MN="):]
		pos = strings.Index(substr, ";")
		if -1 == pos {
			continue
		}
		mn := substr[:pos]

		pos = strings.Index(str, "QN=")
		if -1 == pos {
			response = 0
		} else {
			substr = str[pos+len("QN="):]
			pos = strings.Index(substr, ";")
			if -1 == pos {
				continue
			}
			qn = substr[:pos]
		}

		pos = strings.Index(str, "CN=")
		if -1 == pos {
			continue
		}
		substr = str[pos+len("CN="):]
		pos = strings.Index(substr, ";")
		if -1 == pos {
			continue
		}
		cn := substr[:pos]

		pos = strings.Index(str, "CP=&&")
		if -1 == pos {
			continue
		}
		substr = str[pos+len("CP=&&"):]
		pos = strings.Index(substr, "&&")
		if -1 == pos {
			continue
		}
		cp := substr[:pos]

		if cn == "2011" {
			if false == saveRtdData(cp, mn) {
				continue
			}
		} else if cn == "2031" || cn == "2051" || cn == "2061" {
			if false == saveHisData(cp, mn, cn) {
				continue
			}
		}

		if response == 0 {
			continue
		}

		sendstr := "ST=91;CN=9013;PW=123456;MN=" + mn + ";CP=&&QN=" + qn + "&&"
		sendstr = hjt212Crc(sendstr)

		data = []byte(sendstr)
		_, err = con.Write(data)
		if err != nil {
			Beelog.Notice("%v", err)
			return
		}
	}
}

func saveRtdData(cp, mn string) bool {
	var realdata airmodel.RealData
	var realdataslice = make([]airmodel.RealData, 0, 10)

	realdata.Mn = mn
	realdata.InsertTime = time.Now()

	str := strings.Split(cp, ";")
	for _, v := range str {
		hasdt := strings.HasPrefix(v, "DataTime=")
		if hasdt == true {
			t, ret := strToTime(v[len("DataTime="):])
			if ret == true {
				realdata.DataTime = t
			}
			continue
		}

		rtdstr := strings.Split(v, ",")

		kvstr := strings.Split(rtdstr[0], "=")

		if len(kvstr) != 2 {
			continue
		}

		pos := strings.Index(kvstr[0], "-Rtd")
		if -1 == pos {
			continue
		}
		realdata.Name = kvstr[0][:pos]
		realdata.Value = kvstr[1]
		realdata.Flag = "N"

		realdataslice = append(realdataslice, realdata)
	}

	o := orm.NewOrm()
	_, err := o.InsertMulti(len(realdataslice), realdataslice)
	if err != nil {
		Beelog.Error("orm error: %v", err)
	}

	return true
}

func saveHisData(cp, mn, cn string) bool {
	var hisdata airmodel.HisData
	var hisdataslice = make([]airmodel.HisData, 0, 10)

	hisdata.Mn = mn
	hisdata.InsertTime = time.Now()

	str := strings.Split(cp, ";")
	for _, v := range str {
		hasdt := strings.HasPrefix(v, "DataTime=")
		if hasdt == true {
			t, ret := strToTime(v[len("DataTime="):])
			if ret == true {
				hisdata.DataTime = t
			}
			continue
		}

		hisstr := strings.Split(v, ",")
		if len(hisstr) < 3 {
			continue
		}

		for _, hv := range hisstr {
			kvstr := strings.Split(hv, "=")

			if len(kvstr) != 2 {
				continue
			}

			pos := strings.Index(kvstr[0], "-Max")
			if -1 != pos {
				hisdata.Name = kvstr[0][:pos]
				hisdata.MaxValue = kvstr[1]
				hisdata.Flag = "N"
			}

			pos = strings.Index(kvstr[0], "-Min")
			if -1 != pos {
				hisdata.Name = kvstr[0][:pos]
				hisdata.MinValue = kvstr[1]
				hisdata.Flag = "N"
			}

			pos = strings.Index(kvstr[0], "-Avg")
			if -1 != pos {
				hisdata.Name = kvstr[0][:pos]
				hisdata.AvgValue = kvstr[1]
				hisdata.Flag = "N"
			}
		}

		hisdataslice = append(hisdataslice, hisdata)
	}

	o := orm.NewOrm()
	var err error
	if cn == "2051" {
		var mindataslice = make([]airmodel.MinData, 0, 10)
		for _, v := range hisdataslice {
			var mindata airmodel.MinData
			mindata.Mn = v.Mn
			mindata.Name = v.Name
			mindata.MinValue = v.MinValue
			mindata.MaxValue = v.MaxValue
			mindata.AvgValue = v.AvgValue
			mindata.Flag = v.Flag
			mindata.DataTime = v.DataTime
			mindata.InsertTime = v.InsertTime
			mindataslice = append(mindataslice, mindata)
		}
		_, err = o.InsertMulti(len(mindataslice), mindataslice)
	} else if cn == "2061" {
		var hourdataslice = make([]airmodel.HourData, 0, 10)
		for _, v := range hisdataslice {
			var hourdata airmodel.HourData
			hourdata.Mn = v.Mn
			hourdata.Name = v.Name
			hourdata.MinValue = v.MinValue
			hourdata.MaxValue = v.MaxValue
			hourdata.AvgValue = v.AvgValue
			hourdata.Flag = v.Flag
			hourdata.DataTime = v.DataTime
			hourdata.InsertTime = v.InsertTime
			hourdataslice = append(hourdataslice, hourdata)
		}
		_, err = o.InsertMulti(len(hourdataslice), hourdataslice)
	} else if cn == "2031" {
		var daydataslice = make([]airmodel.DayData, 0, 10)
		for _, v := range hisdataslice {
			var daydata airmodel.DayData
			daydata.Mn = v.Mn
			daydata.Name = v.Name
			daydata.MinValue = v.MinValue
			daydata.MaxValue = v.MaxValue
			daydata.AvgValue = v.AvgValue
			daydata.Flag = v.Flag
			daydata.DataTime = v.DataTime
			daydata.InsertTime = v.InsertTime
			daydataslice = append(daydataslice, daydata)
		}
		_, err = o.InsertMulti(len(daydataslice), daydataslice)
	}

	if err != nil {
		Beelog.Error("orm insert error: %v", err)
	}

	return true
}

func hjt212Crc(str string) string {
	var wCrc uint16 = 0xffff
	var temp uint8

	for _, dc := range str {
		temp = uint8(wCrc >> 8)
		temp ^= uint8(dc)
		wCrc = uint16(temp)
		for j := 0; j < 8; j++ {
			if (wCrc & 0x0001) != 0 {
				wCrc >>= 1
				wCrc &= 0x7fff
				wCrc ^= 0xA001
			} else {
				wCrc >>= 1
				wCrc &= 0x7fff
			}
		}
	}

	head := fmt.Sprintf("##%04d", len(str))
	tail := fmt.Sprintf("%04X\r\n", wCrc)

	retstr := head + str + tail

	return retstr
}

func strToTime(str string) (time.Time, bool) {
	if len(str) != 14 {
		return time.Now(), false
	}

	year, _ := strconv.Atoi(str[:4])
	month, _ := strconv.Atoi(str[4:6])
	day, _ := strconv.Atoi(str[6:8])
	hour, _ := strconv.Atoi(str[8:10])
	min, _ := strconv.Atoi(str[10:12])
	sec, _ := strconv.Atoi(str[12:])

	t := time.Date(year, time.Month(month), day, hour, min, sec, 0, time.Local)

	return t, true
}

func deleteDataFunc() {
	for {
		deleteOldData(0)
		time.Sleep(time.Second * 5)

		deleteOldData(1)
		time.Sleep(time.Second * 5)

		deleteOldData(2)
		time.Sleep(time.Second * 5)

		deleteOldData(3)
		time.Sleep(time.Hour)
	}
}

func deleteOldData(dtype int) bool {
	var table_name string
	var max_table_num int64

	switch dtype {
	case 0:
		max_table_num = MAX_REAL_DATANUM
		table_name = "real_data"
	case 1:
		max_table_num = MAX_MIN_DATANUM
		table_name = "min_data"
	case 2:
		max_table_num = MAX_HOUR_DATANUM
		table_name = "hour_data"
	case 3:
		max_table_num = MAX_DAY_DATANUM
		table_name = "day_data"
	default:
		return false
	}

	sql := fmt.Sprintf("DELETE FROM %s WHERE Id < (SELECT MAX(Id) FROM %s) - %d;",
		table_name, table_name, max_table_num)

	o := orm.NewOrm()
	_, err := o.Raw(sql).Exec()
	if err != nil {
		Beelog.Error("orm delete error: %v", err)
		return false
	}

	return true
}
